//SPDX-FileCopyrightText: Copyright 2022-2024 深圳市同心圆网络有限公司
//SPDX-License-Identifier: GPL-3.0-only

package project_requirement_api_serv

import (
	"context"
	"strings"
	"time"

	"atomgit.com/openlinksaas/api-server/api_common"
	"atomgit.com/openlinksaas/api-server/dao"
	"atomgit.com/openlinksaas/api-server/dao/comment_dao"
	"atomgit.com/openlinksaas/api-server/dao/project_dao"
	"atomgit.com/openlinksaas/api-server/dao/requirement_dao"
	"atomgit.com/openlinksaas/api-server/dao/user_dao"
	"atomgit.com/openlinksaas/api-server/dao/watch_dao"
	"atomgit.com/openlinksaas/api-server/utils"
	"atomgit.com/openlinksaas/proto-gen-go.git/events/events_requirement"
	"atomgit.com/openlinksaas/proto-gen-go.git/notices/notices_requirement"
	"atomgit.com/openlinksaas/proto-gen-go.git/project_api"
	"atomgit.com/openlinksaas/proto-gen-go.git/project_comment_api"
	"atomgit.com/openlinksaas/proto-gen-go.git/project_recycle_api"
	"atomgit.com/openlinksaas/proto-gen-go.git/project_requirement_api"
	"atomgit.com/openlinksaas/proto-gen-go.git/project_watch_api"
	"github.com/google/uuid"
	"go.mongodb.org/mongo-driver/mongo"
)

func (apiImpl *ProjectRequirementApiImpl) CreateRequirement(ctx context.Context, req *project_requirement_api.CreateRequirementRequest) (*project_requirement_api.CreateRequirementResponse, error) {
	if req.BaseInfo.TagIdList == nil {
		req.BaseInfo.TagIdList = []string{}
	}
	sessionItem, ok := dao.CacheDao.GetSession(ctx, req.SessionId)
	if !ok {
		return &project_requirement_api.CreateRequirementResponse{
			Code:   project_requirement_api.CreateRequirementResponse_CODE_WRONG_SESSION,
			ErrMsg: "会话不存在",
		}, nil
	}
	projectItem, _, _, err := utils.GetProjectMemberRole(ctx, req.ProjectId, sessionItem.UserId)
	if err == utils.ErrNoProject {
		return &project_requirement_api.CreateRequirementResponse{
			Code:   project_requirement_api.CreateRequirementResponse_CODE_NO_PROJECT,
			ErrMsg: "项目不存在",
		}, nil
	} else if err == utils.ErrNoRole {
		return &project_requirement_api.CreateRequirementResponse{
			Code:   project_requirement_api.CreateRequirementResponse_CODE_NO_PERMISSION,
			ErrMsg: "没有权限",
		}, nil
	} else if err != nil {
		return nil, err
	}

	req.BaseInfo.Title = strings.Trim(req.BaseInfo.Title, " \t\r\n")
	if req.BaseInfo.Title == "" {
		return &project_requirement_api.CreateRequirementResponse{
			Code:   project_requirement_api.CreateRequirementResponse_CODE_EMPTY_TITLE,
			ErrMsg: "需求标题为空",
		}, nil
	}

	//检查标签
	tagItemList, err := project_dao.TagInfoDao.List(ctx, req.ProjectId, project_api.TAG_SCOPE_TYPE_TAG_SCOPE_REQ)
	if err != nil {
		return nil, err
	}
	tagItemMap := map[string]*project_dao.TagInfoDbItem{}
	for _, tagItem := range tagItemList {
		tagItemMap[tagItem.TagId] = tagItem
	}
	for _, tagId := range req.BaseInfo.TagIdList {
		_, ok = tagItemMap[tagId]
		if !ok {
			return &project_requirement_api.CreateRequirementResponse{
				Code:   project_requirement_api.CreateRequirementResponse_CODE_NO_TAG,
				ErrMsg: "标签不存在",
			}, nil
		}
	}

	requireId := uuid.NewString()
	nowTime := time.Now().UnixNano() / 1e6

	dbSess, err := dao.StartSession()
	if err != nil {
		return nil, err
	}
	defer dao.EndSession(dbSess)

	ret, err := dbSess.WithTransaction(ctx, func(sessCtx mongo.SessionContext) (interface{}, error) {
		err = requirement_dao.RequirementInfoDao.Insert(sessCtx, &requirement_dao.RequirementInfoDbItem{
			RequirementId: requireId,
			ProjectId:     req.ProjectId,
			BaseInfo: requirement_dao.BaseRequirementInfoDbItem{
				Title:     req.BaseInfo.Title,
				Content:   req.BaseInfo.Content,
				TagIdList: req.BaseInfo.TagIdList,
			},
			IssueLinkCount:      0,
			CreateUserId:        sessionItem.UserId,
			CreateTime:          nowTime,
			UpdateUserId:        sessionItem.UserId,
			UpdateTime:          nowTime,
			Closed:              false,
			FourQUrgencyValue:   0.0,
			FourQImportantValue: 0.0,
			KanoStat:            requirement_dao.KanoStatDbItem{},
			KanoBetter:          0.0,
		})
		if err != nil {
			return nil, err
		}
		return &project_requirement_api.CreateRequirementResponse{
			Code:          project_requirement_api.CreateRequirementResponse_CODE_OK,
			RequirementId: requireId,
		}, nil
	})
	if err != nil {
		return nil, err
	}
	//记录事件
	go apiImpl.addCreateRequirementEvent(sessionItem, req, projectItem, requireId, nowTime)
	//发送通知
	go api_common.SendNoticeToProjectScope(context.Background(), req.ProjectId, &notices_requirement.NewRequirementNotice{
		ProjectId:     req.ProjectId,
		RequirementId: requireId,
		CreateUserId:  sessionItem.UserId,
	})
	return ret.(*project_requirement_api.CreateRequirementResponse), nil
}

func (apiImpl *ProjectRequirementApiImpl) ListRequirement(ctx context.Context, req *project_requirement_api.ListRequirementRequest) (*project_requirement_api.ListRequirementResponse, error) {
	emptyList := []*project_requirement_api.RequirementInfo{}

	sessionItem, ok := dao.CacheDao.GetSession(ctx, req.SessionId)
	if !ok {
		return &project_requirement_api.ListRequirementResponse{
			Code:            project_requirement_api.ListRequirementResponse_CODE_WRONG_SESSION,
			ErrMsg:          "会话不存在",
			RequirementList: emptyList,
		}, nil
	}
	projectItem, _, roleItem, err := utils.GetProjectMemberRole(ctx, req.ProjectId, sessionItem.UserId)
	if err == utils.ErrNoProject {
		return &project_requirement_api.ListRequirementResponse{
			Code:            project_requirement_api.ListRequirementResponse_CODE_NO_PROJECT,
			ErrMsg:          "项目不存在",
			RequirementList: emptyList,
		}, nil
	} else if err == utils.ErrNoRole {
		return &project_requirement_api.ListRequirementResponse{
			Code:            project_requirement_api.ListRequirementResponse_CODE_NO_PERMISSION,
			ErrMsg:          "没有权限",
			RequirementList: emptyList,
		}, nil
	} else if err != nil {
		return nil, err
	}

	watchReqIdList := []string{}
	if req.FilterByWatch {
		watchReqIdList, err = watch_dao.MyWatchDao.DistinctTargetId(ctx, req.ProjectId, sessionItem.UserId, uint32(project_watch_api.WATCH_TARGET_TYPE_WATCH_TARGET_REQUIRE_MENT))
		if err != nil {
			return nil, err
		}
	}

	listParam := &requirement_dao.LinkRequirementParam{
		ProjectId:            req.ProjectId,
		FilterByKeyword:      req.FilterByKeyword,
		Keyword:              req.Keyword,
		FilterByHasLinkIssue: req.FilterByHasLinkIssue,
		HasLinkIssue:         req.HasLinkIssue,
		FilterByClosed:       req.FilterByClosed,
		Closed:               req.Closed,
		FilterByTagIdList:    req.FilterByTagIdList,
		TagIdList:            req.TagIdList,
		FilterByWatch:        req.FilterByWatch,
		WatchReqIdList:       watchReqIdList,
	}

	count, err := requirement_dao.RequirementInfoDao.Count(ctx, listParam)
	if err != nil {
		return nil, err
	}
	reqItemList, err := requirement_dao.RequirementInfoDao.List(ctx, listParam, int64(req.Offset), int64(req.Limit), req.SortType)
	if err != nil {
		return nil, err
	}

	//获取关注信息
	reqIdList := []string{}
	for _, reqItem := range reqItemList {
		reqIdList = append(reqIdList, reqItem.RequirementId)
	}
	watchItemMap, watchUserIdList, err := watch_dao.MyWatchDao.CalcWatchUserMap(ctx, req.ProjectId, uint32(project_watch_api.WATCH_TARGET_TYPE_WATCH_TARGET_REQUIRE_MENT), reqIdList)
	if err != nil {
		return nil, err
	}

	//补全用户信息
	userIdList := []string{}
	for _, reqItem := range reqItemList {
		userIdList = append(userIdList, reqItem.CreateUserId)
		userIdList = append(userIdList, reqItem.UpdateUserId)
	}
	userIdList = append(userIdList, watchUserIdList...)

	userItemList, err := user_dao.UserInfoDao.ListById(ctx, userIdList)
	if err != nil {
		return nil, err
	}
	userItemMap := map[string]*user_dao.UserInfoDbItem{}
	for _, userItem := range userItemList {
		userItemMap[userItem.UserId] = userItem
	}
	//获取标签
	tagItemList, err := project_dao.TagInfoDao.List(ctx, req.ProjectId, project_api.TAG_SCOPE_TYPE_TAG_SCOPE_REQ)
	if err != nil {
		return nil, err
	}
	tagItemMap := map[string]*project_dao.TagInfoDbItem{}
	for _, tagItem := range tagItemList {
		tagItemMap[tagItem.TagId] = tagItem
	}
	//生成结果
	infoList := []*project_requirement_api.RequirementInfo{}
	for _, reqItem := range reqItemList {
		info := reqItem.ToRequirementInfo()
		createMember, ok := userItemMap[reqItem.CreateUserId]
		if ok {
			info.CreateDisplayName = createMember.BasicInfo.DisplayName
			info.CreateLogoUri = createMember.BasicInfo.LogoUri
		}
		updateMember, ok := userItemMap[reqItem.UpdateUserId]
		if ok {
			info.UpdateDisplayName = updateMember.BasicInfo.DisplayName
			info.UpdateLogoUri = updateMember.BasicInfo.LogoUri
		}
		tagInfoList := []*project_requirement_api.RequirementTag{}
		for _, tagId := range reqItem.BaseInfo.TagIdList {
			tagItem, ok := tagItemMap[tagId]
			if ok {
				tagInfoList = append(tagInfoList, &project_requirement_api.RequirementTag{
					TagId:   tagId,
					TagName: tagItem.TagName,
					BgColor: tagItem.BgColor,
				})
			}
		}
		info.TagInfoList = tagInfoList
		info.UserRequirementPerm = apiImpl.calcUserPerm(sessionItem, projectItem, roleItem, reqItem)
		watchUserIdList, ok := watchItemMap[reqItem.RequirementId]
		if ok {
			for _, watchUserId := range watchUserIdList {
				if watchUserId == sessionItem.UserId {
					info.MyWatch = true
				}
				userItem, ok := userItemMap[watchUserId]
				if ok {
					info.WatchUserList = append(info.WatchUserList, &project_requirement_api.WatchUser{
						MemberUserId: userItem.UserId,
						DisplayName:  userItem.BasicInfo.DisplayName,
						LogoUri:      userItem.BasicInfo.LogoUri,
					})
				}
			}
		}
		infoList = append(infoList, info)
	}
	return &project_requirement_api.ListRequirementResponse{
		Code:            project_requirement_api.ListRequirementResponse_CODE_OK,
		TotalCount:      count,
		RequirementList: infoList,
	}, nil
}

func (apiImpl *ProjectRequirementApiImpl) ListRequirementById(ctx context.Context, req *project_requirement_api.ListRequirementByIdRequest) (*project_requirement_api.ListRequirementByIdResponse, error) {
	emptyList := []*project_requirement_api.RequirementInfo{}

	sessionItem, ok := dao.CacheDao.GetSession(ctx, req.SessionId)
	if !ok {
		return &project_requirement_api.ListRequirementByIdResponse{
			Code:            project_requirement_api.ListRequirementByIdResponse_CODE_WRONG_SESSION,
			ErrMsg:          "会话不存在",
			RequirementList: emptyList,
		}, nil
	}
	projectItem, _, roleItem, err := utils.GetProjectMemberRole(ctx, req.ProjectId, sessionItem.UserId)
	if err == utils.ErrNoProject {
		return &project_requirement_api.ListRequirementByIdResponse{
			Code:            project_requirement_api.ListRequirementByIdResponse_CODE_NO_PROJECT,
			ErrMsg:          "项目不存在",
			RequirementList: emptyList,
		}, nil
	} else if err == utils.ErrNoRole {
		return &project_requirement_api.ListRequirementByIdResponse{
			Code:            project_requirement_api.ListRequirementByIdResponse_CODE_NO_PERMISSION,
			ErrMsg:          "没有权限",
			RequirementList: emptyList,
		}, nil
	} else if err != nil {
		return nil, err
	}
	reqItemList, err := requirement_dao.RequirementInfoDao.ListById(ctx, req.ProjectId, req.RequirementIdList)
	if err != nil {
		return nil, err
	}

	//获取关注信息
	reqIdList := []string{}
	for _, reqItem := range reqItemList {
		reqIdList = append(reqIdList, reqItem.RequirementId)
	}
	watchItemMap, watchUserIdList, err := watch_dao.MyWatchDao.CalcWatchUserMap(ctx, req.ProjectId, uint32(project_watch_api.WATCH_TARGET_TYPE_WATCH_TARGET_REQUIRE_MENT), reqIdList)
	if err != nil {
		return nil, err
	}

	//补全用户信息
	userIdList := []string{}
	for _, reqItem := range reqItemList {
		userIdList = append(userIdList, reqItem.CreateUserId)
		userIdList = append(userIdList, reqItem.UpdateUserId)
	}
	userIdList = append(userIdList, watchUserIdList...)
	userItemList, err := user_dao.UserInfoDao.ListById(ctx, userIdList)
	if err != nil {
		return nil, err
	}
	userItemMap := map[string]*user_dao.UserInfoDbItem{}
	for _, userItem := range userItemList {
		userItemMap[userItem.UserId] = userItem
	}
	//获取标签
	tagItemList, err := project_dao.TagInfoDao.List(ctx, req.ProjectId, project_api.TAG_SCOPE_TYPE_TAG_SCOPE_REQ)
	if err != nil {
		return nil, err
	}
	tagItemMap := map[string]*project_dao.TagInfoDbItem{}
	for _, tagItem := range tagItemList {
		tagItemMap[tagItem.TagId] = tagItem
	}
	//生成结果
	infoList := []*project_requirement_api.RequirementInfo{}
	for _, reqItem := range reqItemList {
		info := reqItem.ToRequirementInfo()
		createMember, ok := userItemMap[reqItem.CreateUserId]
		if ok {
			info.CreateDisplayName = createMember.BasicInfo.DisplayName
			info.CreateLogoUri = createMember.BasicInfo.LogoUri
		}
		updateMember, ok := userItemMap[reqItem.UpdateUserId]
		if ok {
			info.UpdateDisplayName = updateMember.BasicInfo.DisplayName
			info.UpdateLogoUri = updateMember.BasicInfo.LogoUri
		}
		tagInfoList := []*project_requirement_api.RequirementTag{}
		for _, tagId := range reqItem.BaseInfo.TagIdList {
			tagItem, ok := tagItemMap[tagId]
			if ok {
				tagInfoList = append(tagInfoList, &project_requirement_api.RequirementTag{
					TagId:   tagId,
					TagName: tagItem.TagName,
					BgColor: tagItem.BgColor,
				})
			}
		}
		info.TagInfoList = tagInfoList
		info.UserRequirementPerm = apiImpl.calcUserPerm(sessionItem, projectItem, roleItem, reqItem)
		watchUserIdList, ok := watchItemMap[reqItem.RequirementId]
		if ok {
			for _, watchUserId := range watchUserIdList {
				if watchUserId == sessionItem.UserId {
					info.MyWatch = true
				}
				userItem, ok := userItemMap[watchUserId]
				if ok {
					info.WatchUserList = append(info.WatchUserList, &project_requirement_api.WatchUser{
						MemberUserId: userItem.UserId,
						DisplayName:  userItem.BasicInfo.DisplayName,
						LogoUri:      userItem.BasicInfo.LogoUri,
					})
				}
			}
		}
		infoList = append(infoList, info)
	}
	return &project_requirement_api.ListRequirementByIdResponse{
		Code:            project_requirement_api.ListRequirementByIdResponse_CODE_OK,
		RequirementList: infoList,
	}, nil
}

func (apiImpl *ProjectRequirementApiImpl) GetRequirement(ctx context.Context, req *project_requirement_api.GetRequirementRequest) (*project_requirement_api.GetRequirementResponse, error) {
	emptyInfo := &project_requirement_api.RequirementInfo{
		BaseInfo:      &project_requirement_api.BaseRequirementInfo{},
		WatchUserList: []*project_requirement_api.WatchUser{},
	}

	sessionItem, ok := dao.CacheDao.GetSession(ctx, req.SessionId)
	if !ok {
		return &project_requirement_api.GetRequirementResponse{
			Code:        project_requirement_api.GetRequirementResponse_CODE_WRONG_SESSION,
			ErrMsg:      "会话不存在",
			Requirement: emptyInfo,
		}, nil
	}
	projectItem, _, roleItem, err := utils.GetProjectMemberRole(ctx, req.ProjectId, sessionItem.UserId)
	if err == utils.ErrNoProject {
		return &project_requirement_api.GetRequirementResponse{
			Code:        project_requirement_api.GetRequirementResponse_CODE_NO_PROJECT,
			ErrMsg:      "项目不存在",
			Requirement: emptyInfo,
		}, nil
	} else if err == utils.ErrNoRole {
		return &project_requirement_api.GetRequirementResponse{
			Code:        project_requirement_api.GetRequirementResponse_CODE_NO_PERMISSION,
			ErrMsg:      "没有权限",
			Requirement: emptyInfo,
		}, nil
	} else if err != nil {
		return nil, err
	}

	reqItem, err := requirement_dao.RequirementInfoDao.Get(ctx, req.ProjectId, req.RequirementId)
	if err != nil {
		return &project_requirement_api.GetRequirementResponse{
			Code:        project_requirement_api.GetRequirementResponse_CODE_NO_REQUIRE_MENT,
			ErrMsg:      "需求不存在",
			Requirement: emptyInfo,
		}, nil
	}
	//获取关注信息
	watchItemMap, watchUserIdList, err := watch_dao.MyWatchDao.CalcWatchUserMap(ctx, req.ProjectId, uint32(project_watch_api.WATCH_TARGET_TYPE_WATCH_TARGET_REQUIRE_MENT), []string{req.RequirementId})
	if err != nil {
		return nil, err
	}
	//补充用户信息
	userIdList := []string{reqItem.CreateUserId, reqItem.UpdateUserId}
	userIdList = append(userIdList, watchUserIdList...)
	userItemList, err := user_dao.UserInfoDao.ListById(ctx, userIdList)
	if err != nil {
		return nil, err
	}
	userItemMap := map[string]*user_dao.UserInfoDbItem{}
	for _, userItem := range userItemList {
		userItemMap[userItem.UserId] = userItem
	}
	//获取标签
	tagItemList, err := project_dao.TagInfoDao.List(ctx, req.ProjectId, project_api.TAG_SCOPE_TYPE_TAG_SCOPE_REQ)
	if err != nil {
		return nil, err
	}
	tagItemMap := map[string]*project_dao.TagInfoDbItem{}
	for _, tagItem := range tagItemList {
		tagItemMap[tagItem.TagId] = tagItem
	}
	//生成结果
	info := reqItem.ToRequirementInfo()
	createMember, ok := userItemMap[reqItem.CreateUserId]
	if ok {
		info.CreateDisplayName = createMember.BasicInfo.DisplayName
		info.CreateLogoUri = createMember.BasicInfo.LogoUri
	}
	updateMember, ok := userItemMap[reqItem.UpdateUserId]
	if ok {
		info.UpdateDisplayName = updateMember.BasicInfo.DisplayName
		info.UpdateLogoUri = updateMember.BasicInfo.LogoUri
	}
	tagInfoList := []*project_requirement_api.RequirementTag{}
	for _, tagId := range reqItem.BaseInfo.TagIdList {
		tagItem, ok := tagItemMap[tagId]
		if ok {
			tagInfoList = append(tagInfoList, &project_requirement_api.RequirementTag{
				TagId:   tagId,
				TagName: tagItem.TagName,
				BgColor: tagItem.BgColor,
			})
		}
	}
	info.TagInfoList = tagInfoList
	info.UserRequirementPerm = apiImpl.calcUserPerm(sessionItem, projectItem, roleItem, reqItem)
	watchUserIdList, ok = watchItemMap[reqItem.RequirementId]
	if ok {
		for _, watchUserId := range watchUserIdList {
			if watchUserId == sessionItem.UserId {
				info.MyWatch = true
			}
			userItem, ok := userItemMap[watchUserId]
			if ok {
				info.WatchUserList = append(info.WatchUserList, &project_requirement_api.WatchUser{
					MemberUserId: userItem.UserId,
					DisplayName:  userItem.BasicInfo.DisplayName,
					LogoUri:      userItem.BasicInfo.LogoUri,
				})
			}
		}
	}

	return &project_requirement_api.GetRequirementResponse{
		Code:        project_requirement_api.GetRequirementResponse_CODE_OK,
		Requirement: info,
	}, nil
}

func (apiImpl *ProjectRequirementApiImpl) UpdateRequirement(ctx context.Context, req *project_requirement_api.UpdateRequirementRequest) (*project_requirement_api.UpdateRequirementResponse, error) {
	if req.BaseInfo.TagIdList == nil {
		req.BaseInfo.TagIdList = []string{}
	}
	sessionItem, ok := dao.CacheDao.GetSession(ctx, req.SessionId)
	if !ok {
		return &project_requirement_api.UpdateRequirementResponse{
			Code:   project_requirement_api.UpdateRequirementResponse_CODE_WRONG_SESSION,
			ErrMsg: "会话不存在",
		}, nil
	}
	projectItem, _, roleItem, err := utils.GetProjectMemberRole(ctx, req.ProjectId, sessionItem.UserId)
	if err == utils.ErrNoProject {
		return &project_requirement_api.UpdateRequirementResponse{
			Code:   project_requirement_api.UpdateRequirementResponse_CODE_NO_PROJECT,
			ErrMsg: "项目不存在",
		}, nil
	} else if err == utils.ErrNoRole {
		return &project_requirement_api.UpdateRequirementResponse{
			Code:   project_requirement_api.UpdateRequirementResponse_CODE_NO_PERMISSION,
			ErrMsg: "没有权限",
		}, nil
	} else if err != nil {
		return nil, err
	}

	req.BaseInfo.Title = strings.Trim(req.BaseInfo.Title, " \t\r\n")
	if req.BaseInfo.Title == "" {
		return &project_requirement_api.UpdateRequirementResponse{
			Code:   project_requirement_api.UpdateRequirementResponse_CODE_EMPTY_TITLE,
			ErrMsg: "需求标题为空",
		}, nil
	}

	reqItem, err := requirement_dao.RequirementInfoDao.Get(ctx, req.ProjectId, req.RequirementId)
	if err != nil {
		return &project_requirement_api.UpdateRequirementResponse{
			Code:   project_requirement_api.UpdateRequirementResponse_CODE_NO_REQUIRE_MENT,
			ErrMsg: "需求不存在",
		}, nil
	}
	if !(sessionItem.UserId == projectItem.OwnerUserId || roleItem.BasicInfo.Admin || sessionItem.UserId == reqItem.CreateUserId) {
		return &project_requirement_api.UpdateRequirementResponse{
			Code:   project_requirement_api.UpdateRequirementResponse_CODE_NO_PERMISSION,
			ErrMsg: "没有权限",
		}, nil
	}

	//获取标签
	tagItemList, err := project_dao.TagInfoDao.List(ctx, req.ProjectId, project_api.TAG_SCOPE_TYPE_TAG_SCOPE_REQ)
	if err != nil {
		return nil, err
	}
	tagItemMap := map[string]*project_dao.TagInfoDbItem{}
	for _, tagItem := range tagItemList {
		tagItemMap[tagItem.TagId] = tagItem
	}
	for _, tagId := range req.BaseInfo.TagIdList {
		_, ok = tagItemMap[tagId]
		if !ok {
			return &project_requirement_api.UpdateRequirementResponse{
				Code:   project_requirement_api.UpdateRequirementResponse_CODE_NO_TAG,
				ErrMsg: "标签不存在",
			}, nil
		}
	}

	nowTime := time.Now().UnixNano() / 1e6

	dbSess, err := dao.StartSession()
	if err != nil {
		return nil, err
	}
	defer dao.EndSession(dbSess)

	ret, err := dbSess.WithTransaction(ctx, func(sessCtx mongo.SessionContext) (interface{}, error) {
		err = requirement_dao.RequirementInfoDao.UpdateBaseInfo(sessCtx, req.ProjectId,
			req.RequirementId, &requirement_dao.BaseRequirementInfoDbItem{
				Title:     req.BaseInfo.Title,
				Content:   req.BaseInfo.Content,
				TagIdList: req.BaseInfo.TagIdList,
			}, sessionItem.UserId, nowTime)
		if err != nil {
			return nil, err
		}
		return &project_requirement_api.UpdateRequirementResponse{
			Code: project_requirement_api.UpdateRequirementResponse_CODE_OK,
		}, nil
	})
	if err != nil {
		return nil, err
	}
	//记录事件
	go apiImpl.addUpdateRequirementEvent(sessionItem, req, projectItem, reqItem, nowTime)
	//发送通知
	go api_common.SendNoticeToProjectScope(context.Background(), req.ProjectId, &notices_requirement.UpdateRequirementNotice{
		ProjectId:     req.ProjectId,
		RequirementId: req.RequirementId,
	})
	return ret.(*project_requirement_api.UpdateRequirementResponse), nil
}

func (apiImpl *ProjectRequirementApiImpl) UpdateTagIdList(ctx context.Context, req *project_requirement_api.UpdateTagIdListRequest) (*project_requirement_api.UpdateTagIdListResponse, error) {
	sessionItem, ok := dao.CacheDao.GetSession(ctx, req.SessionId)
	if !ok {
		return &project_requirement_api.UpdateTagIdListResponse{
			Code:   project_requirement_api.UpdateTagIdListResponse_CODE_WRONG_SESSION,
			ErrMsg: "会话不存在",
		}, nil
	}
	projectItem, _, roleItem, err := utils.GetProjectMemberRole(ctx, req.ProjectId, sessionItem.UserId)
	if err == utils.ErrNoProject {
		return &project_requirement_api.UpdateTagIdListResponse{
			Code:   project_requirement_api.UpdateTagIdListResponse_CODE_NO_PROJECT,
			ErrMsg: "项目不存在",
		}, nil
	} else if err == utils.ErrNoRole {
		return &project_requirement_api.UpdateTagIdListResponse{
			Code:   project_requirement_api.UpdateTagIdListResponse_CODE_NO_PERMISSION,
			ErrMsg: "没有权限",
		}, nil
	} else if err != nil {
		return nil, err
	}

	reqItem, err := requirement_dao.RequirementInfoDao.Get(ctx, req.ProjectId, req.RequirementId)
	if err != nil {
		return &project_requirement_api.UpdateTagIdListResponse{
			Code:   project_requirement_api.UpdateTagIdListResponse_CODE_NO_REQUIRE_MENT,
			ErrMsg: "需求不存在",
		}, nil
	}
	if !(sessionItem.UserId == projectItem.OwnerUserId || roleItem.BasicInfo.Admin || sessionItem.UserId == reqItem.CreateUserId) {
		return &project_requirement_api.UpdateTagIdListResponse{
			Code:   project_requirement_api.UpdateTagIdListResponse_CODE_NO_PERMISSION,
			ErrMsg: "没有权限",
		}, nil
	}

	//检查标签
	tagItemList, err := project_dao.TagInfoDao.List(ctx, req.ProjectId, project_api.TAG_SCOPE_TYPE_TAG_SCOPE_REQ)
	if err != nil {
		return nil, err
	}
	tagItemMap := map[string]*project_dao.TagInfoDbItem{}
	for _, tagItem := range tagItemList {
		tagItemMap[tagItem.TagId] = tagItem
	}
	for _, tagId := range req.TagIdList {
		_, ok = tagItemMap[tagId]
		if !ok {
			return &project_requirement_api.UpdateTagIdListResponse{
				Code:   project_requirement_api.UpdateTagIdListResponse_CODE_NO_TAG,
				ErrMsg: "标签不存在",
			}, nil
		}
	}

	oldTagList := []*events_requirement.TagInfo{}
	newTagList := []*events_requirement.TagInfo{}

	for _, tagId := range reqItem.BaseInfo.TagIdList {
		tagItem, ok := tagItemMap[tagId]
		if ok {
			oldTagList = append(oldTagList, &events_requirement.TagInfo{
				TagId:   tagId,
				TagName: tagItem.TagName,
			})
		}
	}
	for _, tagId := range req.TagIdList {
		tagItem, ok := tagItemMap[tagId]
		if ok {
			newTagList = append(newTagList, &events_requirement.TagInfo{
				TagId:   tagId,
				TagName: tagItem.TagName,
			})
		}
	}

	nowTime := time.Now().UnixNano() / 1e6
	dbSess, err := dao.StartSession()
	if err != nil {
		return nil, err
	}
	defer dao.EndSession(dbSess)

	ret, err := dbSess.WithTransaction(ctx, func(sessCtx mongo.SessionContext) (interface{}, error) {
		err = requirement_dao.RequirementInfoDao.UpdateTagIdList(sessCtx, req.ProjectId, req.RequirementId, req.TagIdList, sessionItem.UserId, nowTime)
		if err != nil {
			return nil, err
		}
		return &project_requirement_api.UpdateTagIdListResponse{
			Code: project_requirement_api.UpdateTagIdListResponse_CODE_OK,
		}, nil
	})
	if err != nil {
		return nil, err
	}
	//记录事件
	go apiImpl.addUpdateTagEvent(sessionItem, projectItem, reqItem, nowTime, oldTagList, newTagList)
	//发送通知
	go api_common.SendNoticeToProjectScope(context.Background(), req.ProjectId, &notices_requirement.UpdateRequirementNotice{
		ProjectId:     req.ProjectId,
		RequirementId: req.RequirementId,
	})
	return ret.(*project_requirement_api.UpdateTagIdListResponse), nil
}

func (apiImpl *ProjectRequirementApiImpl) RemoveRequirement(ctx context.Context, req *project_requirement_api.RemoveRequirementRequest) (*project_requirement_api.RemoveRequirementResponse, error) {
	sessionItem, ok := dao.CacheDao.GetSession(ctx, req.SessionId)
	if !ok {
		return &project_requirement_api.RemoveRequirementResponse{
			Code:   project_requirement_api.RemoveRequirementResponse_CODE_WRONG_SESSION,
			ErrMsg: "会话不存在",
		}, nil
	}
	projectItem, _, roleItem, err := utils.GetProjectMemberRole(ctx, req.ProjectId, sessionItem.UserId)
	if err == utils.ErrNoProject {
		return &project_requirement_api.RemoveRequirementResponse{
			Code:   project_requirement_api.RemoveRequirementResponse_CODE_NO_PROJECT,
			ErrMsg: "项目不存在",
		}, nil
	} else if err == utils.ErrNoRole {
		return &project_requirement_api.RemoveRequirementResponse{
			Code:   project_requirement_api.RemoveRequirementResponse_CODE_NO_PERMISSION,
			ErrMsg: "没有权限",
		}, nil
	} else if err != nil {
		return nil, err
	}

	reqItem, err := requirement_dao.RequirementInfoDao.Get(ctx, req.ProjectId, req.RequirementId)
	if err != nil {
		return &project_requirement_api.RemoveRequirementResponse{
			Code:   project_requirement_api.RemoveRequirementResponse_CODE_NO_REQUIRE_MENT,
			ErrMsg: "需求不存在",
		}, nil
	}
	if !(sessionItem.UserId == reqItem.CreateUserId || projectItem.OwnerUserId == sessionItem.UserId || roleItem.BasicInfo.Admin) {
		return &project_requirement_api.RemoveRequirementResponse{
			Code:   project_requirement_api.RemoveRequirementResponse_CODE_NO_PERMISSION,
			ErrMsg: "没有权限",
		}, nil
	}

	nowTime := time.Now().UnixNano() / 1e6

	dbSess, err := dao.StartSession()
	if err != nil {
		return nil, err
	}
	defer dao.EndSession(dbSess)

	ret, err := dbSess.WithTransaction(ctx, func(sessCtx mongo.SessionContext) (interface{}, error) {
		err = requirement_dao.RequirementInfoDao.Remove(sessCtx, req.ProjectId, req.RequirementId)
		if err != nil {
			return nil, err
		}
		err = requirement_dao.RequirementRecycleDao.Insert(sessCtx, reqItem)
		if err != nil {
			return nil, err
		}
		err = project_dao.RecycleDao.Insert(sessCtx, &project_dao.RecycleItemInfoDbItem{
			RecycleItemId:   req.RequirementId,
			ProjectId:       req.ProjectId,
			RecycleItemType: uint32(project_recycle_api.RECYCLE_ITEM_TYPE_RECYCLE_ITEM_REQUIREMENT),
			RemoveUserId:    sessionItem.UserId,
			RemoveTime:      nowTime,
		})
		if err != nil {
			return nil, err
		}
		//删除关注
		err = watch_dao.MyWatchDao.RemoveByTarget(sessCtx, req.ProjectId, uint32(project_watch_api.WATCH_TARGET_TYPE_WATCH_TARGET_REQUIRE_MENT), req.RequirementId)
		if err != nil {
			return nil, err
		}
		//删除未读评论通知
		err = comment_dao.UnReadDao.RemoveByTarget(sessCtx, req.ProjectId, uint32(project_comment_api.COMMENT_TARGET_TYPE_COMMENT_TARGET_REQUIRE_MENT), req.RequirementId)
		if err != nil {
			return nil, err
		}
		return &project_requirement_api.RemoveRequirementResponse{
			Code: project_requirement_api.RemoveRequirementResponse_CODE_OK,
		}, nil
	})
	if err != nil {
		return nil, err
	}
	//记录时事件
	go apiImpl.addRemoveRequirementEvent(sessionItem, projectItem, reqItem, nowTime)
	//发送通知
	go api_common.SendNoticeToProjectScope(context.Background(), req.ProjectId, &notices_requirement.RemoveRequirementNotice{
		ProjectId:     req.ProjectId,
		RequirementId: req.RequirementId,
	})
	return ret.(*project_requirement_api.RemoveRequirementResponse), nil
}

func (apiImpl *ProjectRequirementApiImpl) CloseRequirement(ctx context.Context, req *project_requirement_api.CloseRequirementRequest) (*project_requirement_api.CloseRequirementResponse, error) {
	sessionItem, ok := dao.CacheDao.GetSession(ctx, req.SessionId)
	if !ok {
		return &project_requirement_api.CloseRequirementResponse{
			Code:   project_requirement_api.CloseRequirementResponse_CODE_WRONG_SESSION,
			ErrMsg: "会话不存在",
		}, nil
	}
	projectItem, _, roleItem, err := utils.GetProjectMemberRole(ctx, req.ProjectId, sessionItem.UserId)
	if err == utils.ErrNoProject {
		return &project_requirement_api.CloseRequirementResponse{
			Code:   project_requirement_api.CloseRequirementResponse_CODE_NO_PROJECT,
			ErrMsg: "项目不存在",
		}, nil
	} else if err == utils.ErrNoRole {
		return &project_requirement_api.CloseRequirementResponse{
			Code:   project_requirement_api.CloseRequirementResponse_CODE_NO_PERMISSION,
			ErrMsg: "没有权限",
		}, nil
	} else if err != nil {
		return nil, err
	}
	//检查需求是否存在
	reqItem, err := requirement_dao.RequirementInfoDao.Get(ctx, req.ProjectId, req.RequirementId)
	if err != nil {
		return &project_requirement_api.CloseRequirementResponse{
			Code:   project_requirement_api.CloseRequirementResponse_CODE_NO_REQUIRE_MENT,
			ErrMsg: "需求不存在",
		}, nil
	}
	if reqItem.Closed {
		return &project_requirement_api.CloseRequirementResponse{
			Code: project_requirement_api.CloseRequirementResponse_CODE_OK,
		}, nil
	}
	if !(projectItem.OwnerUserId == sessionItem.UserId || roleItem.BasicInfo.Admin || sessionItem.UserId == reqItem.CreateUserId) {
		return &project_requirement_api.CloseRequirementResponse{
			Code:   project_requirement_api.CloseRequirementResponse_CODE_NO_PERMISSION,
			ErrMsg: "没有权限",
		}, nil
	}

	nowTime := time.Now().UnixNano() / 1e6

	dbSess, err := dao.StartSession()
	if err != nil {
		return nil, err
	}
	defer dao.EndSession(dbSess)

	ret, err := dbSess.WithTransaction(ctx, func(sessCtx mongo.SessionContext) (interface{}, error) {
		err = requirement_dao.RequirementInfoDao.UpdateClosed(sessCtx, req.ProjectId, req.RequirementId, true, sessionItem.UserId, nowTime)
		if err != nil {
			return nil, err
		}
		return &project_requirement_api.CloseRequirementResponse{
			Code: project_requirement_api.CloseRequirementResponse_CODE_OK,
		}, nil
	})
	if err != nil {
		return nil, err
	}
	//记录事件
	go apiImpl.addCloseRequirementEvent(sessionItem, projectItem, reqItem, nowTime)
	//发送通知
	go api_common.SendNoticeToProjectScope(context.Background(), req.ProjectId, &notices_requirement.UpdateRequirementNotice{
		ProjectId:     req.ProjectId,
		RequirementId: req.RequirementId,
	})
	return ret.(*project_requirement_api.CloseRequirementResponse), nil
}

func (apiImpl *ProjectRequirementApiImpl) OpenRequirement(ctx context.Context, req *project_requirement_api.OpenRequirementRequest) (*project_requirement_api.OpenRequirementResponse, error) {
	sessionItem, ok := dao.CacheDao.GetSession(ctx, req.SessionId)
	if !ok {
		return &project_requirement_api.OpenRequirementResponse{
			Code:   project_requirement_api.OpenRequirementResponse_CODE_WRONG_SESSION,
			ErrMsg: "会话不存在",
		}, nil
	}
	projectItem, _, roleItem, err := utils.GetProjectMemberRole(ctx, req.ProjectId, sessionItem.UserId)
	if err == utils.ErrNoProject {
		return &project_requirement_api.OpenRequirementResponse{
			Code:   project_requirement_api.OpenRequirementResponse_CODE_NO_PROJECT,
			ErrMsg: "项目不存在",
		}, nil
	} else if err == utils.ErrNoRole {
		return &project_requirement_api.OpenRequirementResponse{
			Code:   project_requirement_api.OpenRequirementResponse_CODE_NO_PERMISSION,
			ErrMsg: "没有权限",
		}, nil
	} else if err != nil {
		return nil, err
	}
	//检查需求是否存在
	reqItem, err := requirement_dao.RequirementInfoDao.Get(ctx, req.ProjectId, req.RequirementId)
	if err != nil {
		return &project_requirement_api.OpenRequirementResponse{
			Code:   project_requirement_api.OpenRequirementResponse_CODE_NO_REQUIRE_MENT,
			ErrMsg: "需求不存在",
		}, nil
	}
	if !reqItem.Closed {
		return &project_requirement_api.OpenRequirementResponse{
			Code: project_requirement_api.OpenRequirementResponse_CODE_OK,
		}, nil
	}
	if !(projectItem.OwnerUserId == sessionItem.UserId || roleItem.BasicInfo.Admin || sessionItem.UserId == reqItem.CreateUserId) {
		return &project_requirement_api.OpenRequirementResponse{
			Code:   project_requirement_api.OpenRequirementResponse_CODE_NO_PERMISSION,
			ErrMsg: "没有权限",
		}, nil
	}

	nowTime := time.Now().UnixNano() / 1e6

	dbSess, err := dao.StartSession()
	if err != nil {
		return nil, err
	}
	defer dao.EndSession(dbSess)

	ret, err := dbSess.WithTransaction(ctx, func(sessCtx mongo.SessionContext) (interface{}, error) {
		err = requirement_dao.RequirementInfoDao.UpdateClosed(sessCtx, req.ProjectId, req.RequirementId, false, sessionItem.UserId, nowTime)
		if err != nil {
			return nil, err
		}
		return &project_requirement_api.OpenRequirementResponse{
			Code: project_requirement_api.OpenRequirementResponse_CODE_OK,
		}, nil
	})
	if err != nil {
		return nil, err
	}
	//记录事件
	go apiImpl.addOpenRequirementEvent(sessionItem, projectItem, reqItem, nowTime)
	//发送通知
	go api_common.SendNoticeToProjectScope(context.Background(), req.ProjectId, &notices_requirement.UpdateRequirementNotice{
		ProjectId:     req.ProjectId,
		RequirementId: req.RequirementId,
	})
	return ret.(*project_requirement_api.OpenRequirementResponse), nil
}

func (apiImpl *ProjectRequirementApiImpl) calcUserPerm(sessionItem *dao.SessionDbItem,
	projectItem *project_dao.ProjectInfoDbItem,
	roleItem *project_dao.RoleInfoDbItem,
	reqItem *requirement_dao.RequirementInfoDbItem) *project_requirement_api.UserRequirementPerm {
	if projectItem.Closed {
		return &project_requirement_api.UserRequirementPerm{}
	}
	if sessionItem.UserId == projectItem.OwnerUserId || roleItem.BasicInfo.Admin || sessionItem.UserId == reqItem.CreateUserId {
		return &project_requirement_api.UserRequirementPerm{
			CanUpdate: reqItem.Closed == false,
			CanRemove: true,
			CanOpen:   reqItem.Closed,
			CanClose:  !reqItem.Closed,
		}
	}
	return &project_requirement_api.UserRequirementPerm{}
}
